package top.chatgqt.SmartChargeStation.demo.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.util.CharsetUtil;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import top.chatgqt.SmartChargeStation.demo.netty.handlers.ImoocServerHeartBeatHandler;
import top.chatgqt.SmartChargeStation.demo.netty.handlers.ImoocServerProtobufHandler;
import top.chatgqt.SmartChargeStation.demo.protobuf.UserProtobuf;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;

/**
 * Netty 服务端
 */
@Slf4j
@Component
@Order(1)
public class NettyServer implements CommandLineRunner {

    private NioEventLoopGroup bossGroup;
    private NioEventLoopGroup workerGroup;
    private ChannelFuture channelFuture;

    @Value("${Netty.server.port}")
    private int port;

    @Async
    @Override
    public void run(String... args) {
        try {
            this.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Netty启动
     */
    private void start() {

        /*
        Netty 对NIO（主从Reactor模型）的深度封装

        1. NioEventLoop 网络指挥官
        2. Channel 通道
        3. ChannelPipline 工作流水线
        4. ChannelHandler 流水线上的员工
        5. ByteBuf 数据容器
         */

        // 处理网络请求
        bossGroup = new NioEventLoopGroup();
        // 处理网络IO
        workerGroup = new NioEventLoopGroup();

        /*
        Bootstrap 中文名称 “引导”
        ServerBootstrap对象的作用是：
            Netty整个程序的组件初始化，启动，服务器连接等等
         */

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap
                // 配置NioEventLoop组件
                .group(bossGroup, workerGroup)
                /*
                SocketChannel 表示：
                    基于Socket通讯的通道
                    监听TCP连接
                 */
                // 配置Channel组件
                .channel(NioServerSocketChannel.class)

                // 设置指定大小的接收缓冲区（TCP）
//                .option(ChannelOption.SO_RCVBUF, 2)

                // 将ChannelHandler添加到ChannelInitializer
                .childHandler(new ChannelInitializer<>() {

                    // Channel初始化
                    @Override
                    protected void initChannel(Channel channel) throws Exception {
                        /*
                        Channel初始化是伴随ChannelPipeline的初始化
                         */

                        //  获取 ChannelPipeline
                        ChannelPipeline pipeline = channel.pipeline();

                        /*
                        真正进行业务逻辑处理
                         */
                        ByteBuf delimiter = Unpooled.copiedBuffer("$_", CharsetUtil.UTF_8);
                        pipeline
                                //.addLast(new DelimiterBasedFrameDecoder(1024, delimiter))
//                                .addLast(new FixedLengthFrameDecoder(11))
                                //.addLast(new StringDecoder())
                                // 添加处理器
//                                .addLast(new ImoocServerHandler())
//                                .addLast(new ImoocServerHandlerAdapter());
                                //.addLast(new ImoocServerPkgHandler());


                                // 发送心跳包
                                /*
                                IdleStateHandler：空闲状态检测器
                                int readerIdleTimeSeconds,：读操作空闲时间，触发名叫READ_IDLE的IdleStateEvent事件
                                int writerIdleTimeSeconds,：写操作空闲时间，WRITE_IDLE
                                int allIdleTimeSeconds,：读写操作空闲时间，ALL_IDLE
                                TimeUnit unit：时间单位

                                这个处理器只是检测空闲时间，具体的业务逻辑需要另外实现

                                当发生读写超时
                                IdleStateHandler会调用fireUserEventTriggered(evt),
                                将IdleStateEvent事件传递到下一个Handler
                                 */
                                .addLast(new IdleStateHandler(60, 60, 60, TimeUnit.SECONDS))
                                // 超时处理逻辑
                                .addLast(new ImoocServerHeartBeatHandler())

                                .addLast(new ProtobufVarint32FrameDecoder())
                                .addLast(new ProtobufDecoder(UserProtobuf.User.getDefaultInstance()))
                                .addLast(new ImoocServerProtobufHandler());
                    }
                });

        /*
        Netty所有操作都是异步的
        会返回Future对象
        可以利用这个对象可以实现异步操作的通知
         */

        // 绑定服务器直到绑定成功为止
        try {
            // 这个阻塞方法比较短暂，当服务端初始化完成后，阻塞就结束
            channelFuture = bootstrap.bind(port).sync();
            log.info("Netty服务端监听的端口" + port);

            if (channelFuture != null && channelFuture.isSuccess()) {
                log.info(">>>> Netty 服务端启动成功");
            }

            /*
            closeFuture().sync();也是阻塞方法
            这个阻塞方法的作用是：
            将Netty服务端的线程状态设为wait
            那么SpringBoot主线程不会执行finally的方法
             */
            if (channelFuture != null) {
                channelFuture.channel().closeFuture().sync();
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            destroy();
        }
    }


    /**
     * Netty 关闭 释放资源
     */
    @PreDestroy
    private void destroy() {
        try {
            bossGroup.shutdownGracefully().sync();
            workerGroup.shutdownGracefully().sync();
            // 关闭通道（若已绑定成功）
            channelFuture.channel().close().sync();

            log.info(">>>>  Netty服务器资源已释放");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
